﻿//-------------------------------------------------------
// Copyright (c)
// All rights reserved.
// 
// File Name: IOCPServerModel.h 
// File Des: socket与完成端口绑定的server类封装
// File Summary: 
/* 这个类CIOCPServerModel是本代码的核心类，用于说明WinSock服务器端编程模型中的
	完成端口(IOCP)的使用方法，并使用MFC对话框程序来调用这个类实现了基本的
	服务器网络通信的功能。
	其中的PER_IO_DATA结构体是封装了用于每一个重叠操作的参数
	PER_HANDLE_DATA 是封装了用于每一个Socket的参数，也就是用于每一个完成端口的参数 
	具体讲明了服务器端建立完成端口、建立工作者线程、投递Recv请求、投递Accept请求的方法，
	所有的客户端连入的Socket都需要绑定到IOCP上	*/
// Cur Version: 1.0
// Author:
// Create Data:
// History:
//     <Author>      <Time>       <Version>      <Des>
//      lzlong     2024-1-11        1.0            
//-------------------------------------------------------

#pragma once

// winsock 2 的头文件和库
#include <winsock2.h>
#include <MSWSock.h>
#include <assert.h>
#include <string>
#include "misc/CVecT.h"
#include "IoCompletionPort.h"
#pragma comment(lib,"ws2_32.lib")

// 缓冲区长度 (1024*4)
// 如果确实客户端发来的每组数据都比较少，那么就设置得小一些，省内存
#define BUFF_SIZE        (1024*4)
// 默认端口
#define DEFAULT_PORT          12345
// 默认IP地址
#define DEFAULT_IP            ("2.0.0.1")

/// <summary>
/// 在完成端口上投递的I/O操作的类型
/// </summary>
typedef enum _OPERATION_TYPE
{
	NULL_POSTED,                       // 用于初始化，无意义
	ACCEPT_POSTED,                     // 标志投递的Accept操作
	SEND_POSTED,                       // 标志投递的是发送操作
	RECV_POSTED,                       // 标志投递的是接收操作
	EXIT_POSTED                        // 标志退出操作
}OPERATION_TYPE;

enum EmNetIOCPErrorType
{
	NET_IOCP_OK = 200,
	NET_IOCP_Sock,         // 201
	NET_IOCP_Bind,         // 202
	NET_IOCP_Listen,       // 203
	NET_IOCP_CreateThread, // 204
	NET_IOCP_InvalidPort,  // 205,
	NET_IOCP_CreateIoCompletionPort, // 206,
	NET_IOCP_WSAStartup,   // 207
	NET_IOCP_WSAIOCTL      // 208
};

/// <summary>
/// 单IO数据结构体定义(用于每一个重叠操作的参数)
/// </summary>
typedef struct _PER_IO_CONTEXT : public OVERLAPPED
{
	SOCKET         m_sockAccept;                               // 这个网络操作所使用的Socket
	WSABUF         m_wsaBuf;                                   // WSA类型的缓冲区，用于给重叠操作传参数的
	OPERATION_TYPE m_OpType;                                   // 标识网络操作的类型(对应上面的枚举)
	UINT           m_uiConnectID;                              // 连接ID
	UINT           m_uiBytesRecved;                            // 已经接收到的size

	// 初始化
	_PER_IO_CONTEXT()
	{
		ZeroMemory(this, sizeof(OVERLAPPED));
		m_sockAccept = INVALID_SOCKET;
		m_wsaBuf.buf = (CHAR*)::HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, BUFF_SIZE);
		m_wsaBuf.len = BUFF_SIZE;
		m_uiConnectID = 0;
		m_uiBytesRecved = 0;
		m_OpType     = NULL_POSTED;
	}
	// 释放掉Socket
	~_PER_IO_CONTEXT()
	{
		static int i = 0;
		printf("~_PER_IO_CONTEXT index:%d\n", i++);

		if (m_sockAccept != INVALID_SOCKET)
		{
			closesocket(m_sockAccept);
			m_sockAccept = INVALID_SOCKET;
		}
		if (m_wsaBuf.buf)
		{
			::HeapFree(GetProcessHeap(), 0, m_wsaBuf.buf);
		}
	}
	// 重置缓冲区内容
	void ResetBuffer()
	{
		if (m_wsaBuf.buf != NULL)
			ZeroMemory(m_wsaBuf.buf, BUFF_SIZE);
		else
			m_wsaBuf.buf = (CHAR*)::HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, BUFF_SIZE);

		ZeroMemory(this, sizeof(OVERLAPPED));
		m_wsaBuf.len = BUFF_SIZE;
		m_uiBytesRecved = 0;
		m_OpType = NULL_POSTED;
	}
} PER_IO_CONTEXT, *LPPER_IO_CONTEXT;

/// <summary>
/// 单句柄数据结构体的内存池
/// </summary>
typedef class _PER_IO_CONTEXT_POOL
	: public CVecT<LPPER_IO_CONTEXT>
{
public:
	_PER_IO_CONTEXT_POOL() {}
	~_PER_IO_CONTEXT_POOL() {}

	LPPER_IO_CONTEXT GetNewObj()
	{
		LPPER_IO_CONTEXT obj = NULL;
		if (GetObj(0, obj))
		{
			RemoveObj(obj); // 移除但不销毁内存
			return obj;
		}
		return NULL;
	}

	bool AddObjToPool(const LPPER_IO_CONTEXT& obj)
	{
		printf("add obj to pool:%d\n", GetCount());
		return AddObj(obj);
	}
}PER_IO_CONTEXT_POOL, *LPPER_IO_CONTEXT_POOL;

/// <summary>
/// 单句柄数据结构体定义(用于每一个完成端口，也就是每一个Socket的参数)
/// </summary>
typedef class _PER_SOCKET_CONTEXT
	: public CVecT<LPPER_IO_CONTEXT>
{
public:
	_PER_SOCKET_CONTEXT()
	{
		m_Socket = INVALID_SOCKET;
		m_uiConnectID = 0;
		m_lPendingNum = 0;
		m_llRecvTotalSize = 0;
		m_uiSeq = 0;
		memset(&m_ClientAddr, 0, sizeof(m_ClientAddr));
	}

	// 释放资源
	~_PER_SOCKET_CONTEXT()
	{
		if (m_Socket != INVALID_SOCKET)
		{
			closesocket(m_Socket);
			m_Socket = INVALID_SOCKET;
		}
		// 释放掉所有的IO上下文数据
		RemoveAll();
	}

	void PreVecObjRemove(const LPPER_IO_CONTEXT& obj) override {
		/// 外部子类重载
		delete obj;
	}

	// 获取一个新的IoContext
	_PER_IO_CONTEXT* GetNewIoContext()
	{
		_PER_IO_CONTEXT* pObj = NULL;
		pObj = m_contextPool.GetNewObj();
		if (!pObj)
		{
			pObj = new _PER_IO_CONTEXT;
		}
		AddObj(pObj);
		return pObj;
	}

    // 加入到内存池
	bool AddToContextPool(const LPPER_IO_CONTEXT& obj)
	{
		obj->ResetBuffer();
		return m_contextPool.AddObjToPool(obj);
	}

public:
	PER_IO_CONTEXT_POOL m_contextPool;                      // 每个io连接的内存池对象
	SOCKET       m_Socket;                                  // 每一个客户端连接的Socket
	SOCKADDR_IN  m_ClientAddr;                              // 客户端的地址
	UINT         m_uiConnectID;                             // 连接ID
	LONG         m_lPendingNum;                             // 发送正在投递出去的数量
	LONGLONG     m_llRecvTotalSize;                         // 当前连接收到的大小
	UINT         m_uiSeq;                                   // 第n个包
}PER_SOCKET_CONTEXT, * LPPER_SOCKET_CONTEXT;

/// <summary>
/// 回调类
/// </summary>
typedef class _IOCPServerCallBack
{
public:
	// 事件通知函数(派生类重载此族函数)
	// 新连接
	virtual void OnConnectionEstablished(LPPER_SOCKET_CONTEXT pSocketContext) = 0;
	// 连接关闭
	virtual void OnConnectionClosed(LPPER_SOCKET_CONTEXT pSocketContext) = 0;
	// 错误提示
	virtual void OnOutPutMsg(const std::wstring strErrorMsg, ...) = 0;
	// 连接上发生错误
	virtual void OnConnectionError(LPPER_SOCKET_CONTEXT pSocketContext, int error) = 0;
	// 读操作完成
	virtual void OnRecvCompleted(LPPER_SOCKET_CONTEXT pSocketContext, LPPER_IO_CONTEXT pIoContext, UINT uiBytesTrans) = 0;
	// 写操作完成
	virtual void OnSendCompleted(LPPER_SOCKET_CONTEXT pSocketContext, LPPER_IO_CONTEXT pIoContext, UINT uiBytesTrans) = 0;
	// IOCP的一些失败
	virtual void OnIOCPCaughtError(INT iIOCPErr, INT iWSAErr) = 0;
	// IOCP初始化ok
	virtual void OnIOCPInitializeFinished() = 0;
}IOCPServerCallBack, * LPIOCPServerCallBack;

/// <summary>
/// CIOCPServerModel类定义
/// </summary>
class CIOCPServerModel : public CVecT<LPPER_SOCKET_CONTEXT>
{
public:
	CIOCPServerModel(LPCSTR lpAddress = DEFAULT_IP, int nPort = DEFAULT_PORT, LPIOCPServerCallBack lpCb= NULL);

	~CIOCPServerModel(void);
public:
	// 启动服务器
	BOOL Start();

	//	停止服务器
	BOOL Stop();

	// 加载Socket库
	bool LoadSocketLib();

	// 卸载Socket库，彻底完事
	void UnloadSocketLib();

	// 设置回调接口类
	void SetIOCPServerCallBackPtr(LPIOCPServerCallBack pIocpSrvCb);

	// 获取回调接口
	LPIOCPServerCallBack GetIOCPServerCallBackPtr();

	// 获得本机的IP地址
	std::string GetLocalIP();

	// 获取设置的ip
	LPCSTR GetListenIP();

	// 获取设置的端口
	INT GetPort();

	// 设置监听IP
	void SetListenIP(LPCSTR lpIP);

	// 设置监听端口
	void SetPort(const INT& nPort);

	/// <summary>
	///  通过连接ID断开连接
	/// </summary>
	/// <param name="uiConID">连接id</param>
	/// <returns>是否成功</returns>
	BOOL DisconnectByConnectID(UINT uiConID);

	/// <summary>
	///  通过连接ID发送数据信息
	/// </summary>
	/// <param name="uiConID">连接id</param>
	/// <param name="lpData">数据buffer</param>
	/// <param name="ulDataSize">数据大小</param>
	/// <returns>是否发送成功</returns>
	BOOL SendDataByConnectID(UINT uiConID, LPCSTR lpData, ULONG ulDataSize);

	/// 外部子类重载
	void PreVecObjRemove(const LPPER_SOCKET_CONTEXT& obj) override;

protected:
	// 初始化IOCP
	bool _InitializeIOCP();

	// 初始化Socket
	bool _InitializeListenSocket();

	// 最后释放资源
	void _DeInitialize();

	// 投递Accept请求
	bool _PostAccept(LPPER_IO_CONTEXT pAcceptIoContext);

	// 投递接收数据请求
	bool _PostRecv(LPPER_IO_CONTEXT pIoContext);

	// 投递发送数据请求
	BOOL _PostSend(LPPER_SOCKET_CONTEXT pSocketContext, LPPER_IO_CONTEXT ioContext, LPWSABUF lpWSABuf);

	// 在有客户端连入的时候，进行处理
	bool _DoAccpet(LPPER_SOCKET_CONTEXT pSocketContext, LPPER_IO_CONTEXT pIoContext);

	// 在有接收的数据到达的时候，进行处理
	bool _DoRecv(LPPER_SOCKET_CONTEXT pSocketContext, LPPER_IO_CONTEXT pIoContext, UINT uiBytesTrans);

	// 在有发送的数据到达的时候，进行处理
	bool _DoSend(LPPER_SOCKET_CONTEXT pSocketContext, LPPER_IO_CONTEXT pIoContext, UINT uiBytesTrans);

	// 将客户端的相关信息存储到数组中
	void _AddToContextList(LPPER_SOCKET_CONTEXT pSocketContext);

	// 将客户端的信息从数组中移除
	void _RemoveContext(LPPER_SOCKET_CONTEXT pSocketContext);

	// 清空客户端信息
	void _ClearContextList();

	// 处理完成端口上的错误
	BOOL HandleError(LPPER_SOCKET_CONTEXT pContext, const DWORD& dwErr);

	// 线程函数，为IOCP请求服务的工作者线程
	static unsigned WINAPI _WorkerThread(LPVOID lpParam);

	// 获得本机的处理器数量
	int _GetNoOfProcessors();

	// 判断客户端Socket是否已经断开
	bool _IsSocketAlive(SOCKET s);

private:
	CIOCP                        m_iocp;                        // 完成端口对象

	LPIOCPServerCallBack         m_IocpSrvCbPtr;                // 回调类指针

	HANDLE                       m_hShutdownEvent;              // 用来通知线程系统退出的事件，为了能够更好的退出线程

	HANDLE*                      m_phWorkerThreads;             // 工作者线程的句柄指针

	INT		                     m_nThreads;                    // 生成的线程数量

	std::string                  m_strIP;                       // 服务器端的IP地址

	INT                          m_nPort;                       // 服务器端的监听端口

	PER_SOCKET_CONTEXT*          m_pListenContext;              // 用于监听的Socket的Context信息

	LPFN_ACCEPTEX                m_lpfnAcceptEx;                // AcceptEx 和 GetAcceptExSockaddrs 的函数指针，用于调用这两个扩展函数

	LPFN_GETACCEPTEXSOCKADDRS    m_lpfnGetAcceptExSockAddrs;

	static UINT                  ms_uiConnectID;                // 连接ID

	BOOL                         m_bServerStarted;               // 是否已经启动
};